import math

class Ray(object):
    def __init__(self, origin, direction):
        self.origin = origin # point
        self.direction = direction.normalized() # vector

    def __repr__(self):
        return 'Ray(%s,%s)' % (repr(self.origin), repr(self.direction))

    def pointAtParameter(self, t):
        return self.origin + self.direction.scale(t)

class Point(object):
    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

    def __repr__(self):
        return 'P(%s,%s,%s)' % (repr(self.x),repr(self.y),repr(self.z))

    def __sub__(self, ptr):
        x = self.x - ptr.x
        y = self.y - ptr.y
        z = self.z - ptr.z
        return Vector(x,y,z)

    def isZero(self):
        if self.x != 0 and self.y != 0 and self.z != 0:
            return False
        else:
            return True

class Vector(object):
    def __init__(self, x, y, z, origin = Point(0,0,0)):
        if origin.isZero():
            self.x = x
            self.y = y
            self.z = z
        else:
            self.x = x - origin.x
            self.y = y - origin.y
            self.z = z - origin.z

    def __repr__(self):
        return 'V(%s,%s,%s)' % (repr(self.x),repr(self.y),repr(self.z))

    def __add__(self, vec):
        x = self.x + vec.x
        y = self.y + vec.y
        z = self.z + vec.z
        return Vector(Point(x,y,z))

    def __sub__(self, vec):
        x = self.x - vec.x
        y = self.y - vec.y
        z = self.z - vec.z
        return Vector(x,y,z)

    def __mul__(self, scalar):
        x = self.x * scalar
        y = self.y * scalar
        z = self.z * scalar
        return Vector(x,y,z)

    def __div__(self, scalar):
        x = self.x / scalar
        y = self.y / scalar
        z = self.z / scalar

    def cross(a, b):
        x = a.y * b.z - a.z * b.y
        y = a.z * b.x - a.x * b.z
        z = a.x * b.y - a.y * b.x
        return Vector(x,y,z)

    def lenght(self):
        return math.sqrt(self.x**2 + self.y**2 + self.z**2)

    def normalized(self):
        x = self.x * (1/length())
        y = self.y * (1/length())
        z = self.z * (1/length())
        return Vector(x,y,z)


class Sphere(object):
    def __init__(self, center, radius):
        self.center = center # point
        self.radius = radius # scalar

    def __repr__(self):
        return 'Sphere(%s,%s)' % (repr(self.center), self.radius)

    def intersectionParameter(self, ray):
        co = self.center - ray.origin
        v = co.dot(ray.direction)
        discriminant = (self.radius * self.radius) - (co.dot(co) - v*v)
        if discriminant < 0:
            return None
        else:
            return v - math.sqrt(discriminant)

    def normalAt(self, p):
        return (p - self.center).normalized()
        


class Plane(object):
    def __init__(self, point, normal):
        self.point = point # point
        self.normal = normal.normalized() # vector

    def __repr__(self):
        return 'Plane(%s,%s)' % (repr(self.point), repr(self.normal))

    def intersectionParameter(self, ray):
        op = ray.origin - self.point
        a = op.dot(self.normal)
        b = ray.direction.dot(self.normal)
        if b:
            return -a/b
        else:
            return None

    def normalAt(self, p):
        return self.normal



class Triangle(object):
    def __init__(self, a, b, c):
        self.a = a # point
        self.b = b # point
        self.c = c # point
        self.u = self.b - self.a # direction vector
        self.v = self.c - self.a # direction vector

    def __repr__(self):
        return 'Triangle(%s,%s,%s)' % (repr(self.a), repr(self.b), repr(self.c))

    def intersectionParameter(self, ray):
        w = ray.origin - self.a
        dv = ray.direction.cross(self.v)
        dvu = dv.dot(self.u)
        if dvu == 0.0:
            return None
        wu = w.cross(self.u)
        r = dv.dot(w) / dvu
        s = wu.dot(ray.direction) / dvu
        if 0<=r and r<=1 and 0<=s and s<=1 and r+s <=1:
            return wu.dot(self.v) / dvu
        else:
            return None
         
    def normalAt(self, p):
        return self.u.cross(self.v).normalized()



class CheckerboardMaterial(object):
    def __init__(self):
        self.baseColor = (1,1,1)
        self.otherColor = (0,0,0)
        self.ambientCoefficient = 1.0 
        self.diffuseCoefficient =  0.6
        self.specularCoefficient = 0.2
        self.checkSize = 1

    def baseColourAt(self, p):
        v = Vector(p)
        v.scale(1.0 / self.checkSize)
        if (int(abs(v.x) + 0.5) + int(abs(v.y) + 0.5) + int(abs(v.z) + 0.5)) % 2:
            return self.otherColour
        return self.baseColour

class Light(object):
    def __init__(self, origin):
        self.origin = origin

class Camera(object):
    def __init__(self, point, direction, angle, aspectratio):
        self.e = point          #point
        self.c = direction      #point
        self.f = (self.c - self.e) / Vector.lenght(self.c - self.e)   #vector
        print self.f
        #self.s = Vector.cross(self.f,Vector(0,1,0)) / Vector.length(Vector.cross(self.f,Vector(0,1,0))) #vector
        #self.u = Vector.cross(self.s,self.f) #vector
        self.alpha = angle/2
        self.height = 2 * math.tan(alpha)
        self.width = aspectratio * height

    def calcRay(x,y):
        pixelWidth = self.width / (wRes-1)
        pixelHeight = self.height / (hRes-1)

        xcomp = self.s * (x * pixelWidth - width / 2)
        ycomp = self.u * (y * pixelHeight - height / 2)

        return Ray(e, f + xcomp + ycomp) # evtl mehrere Strahel pro Pixel
        
    
    def rayCasting(image):
        for x in range(imageWidth):
            for y in range(imageHeight):
                ray = calcRay(x,y)
                maxdist = float('inf')
                color = BACKGROUND_COLOR
                for object in objectlist:
                    hitdist = object.intersectionParameter(ray)
                    if hitdist:
                        if hitdist < maxdist:
                            maxdist = hitdist
                            color = object.colorAt(ray)
                image.putpixel((x,y), color)

        return image
        











        

        
